package helperx

import (
	"bytes"
	"context"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

// // 表单请求
//	form := url.Values{}
//	form.Add("a", "b")
//	form.Add("c", "d")
//	body := form.Encode()
//	resp, err := SendHttpRequest("POST", "http://example", map[string]string{"a":"b"}, []byte(body), true)
//
//	// json 请求
//	j := `{"a":"b"}`
//	resp, err := SendHttpRequest("POST", "http://example", map[string]string{"a":"b"}, []byte(j), false)
//
//	// get 请求
//	resp, err := SendHttpRequest("GET", "http://example", map[string]string{"a":"b"}, nil, false, 0)

// SendHttpRequest 发送请求
// 可发送json请求或post form请求
// post form请求时 body必须是经过 url.Values{} Encode() 的
// timeout 传0的话，默认5秒
func SendHttpRequest(c context.Context, method, reqUrl string, headers map[string]string, body []byte, isForm bool, timeout time.Duration) ([]byte, error) {
	if headers == nil {
		headers = map[string]string{}
	}
	headers["caller"] = Callers(3)

	requestId := c.Value("RequestId")
	if v, ok := requestId.(string); ok && v != "" {
		headers["X-Request-Id"] = v
	}

	method = strings.ToUpper(method)
	if method == "GET" {
		return sendGetRequest(reqUrl, headers, timeout)
	}

	if method == "POST" || method == "PUT" || method == "PATCH" || method == "DELETE" {
		return sendOtherRequest(method, reqUrl, headers, body, isForm, timeout)
	}

	return nil, errors.New("unsupported method")
}

func sendGetRequest(reqUrl string, headers map[string]string, timeout time.Duration) ([]byte, error) {
	req, err := http.NewRequest("GET", reqUrl, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", "application/json; charset=UTF-8")

	for k, v := range headers {
		if strings.ToLower(k) != "content-type" {
			req.Header.Add(k, v)
		}
	}

	return doRequest(req, timeout)
}

func sendOtherRequest(method, reqUrl string, headers map[string]string, body []byte, isForm bool, timeout time.Duration) ([]byte, error) {
	req, err := http.NewRequest(method, reqUrl, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}

	if isForm {
		req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	} else {
		req.Header.Add("Content-Type", "application/json; charset=UTF-8")
	}
	for k, v := range headers {
		if strings.ToLower(k) != "content-type" {
			req.Header.Add(k, v)
		}
	}

	return doRequest(req, timeout)
}

func doRequest(req *http.Request, timeout time.Duration) ([]byte, error) {
	t := 5 * time.Second
	if timeout > 0 {
		t = timeout
	}

	cli := http.Client{Timeout: t}

	resp, err := cli.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	res, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return res, nil
}
